#ifndef _friend_class_h_
#define _friend_class_h_
#include "data_struct.h"
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <regex>
#include <strstream>
#include <vector>
#include <sstream>
#include <algorithm>
#include <numeric>
using namespace std;


class DivideConquer;

class BasicStack
{
public:
	BasicStack(int32 stack_size)
	{
		stack_.reserve(stack_size);
		head_ = 0;
		tail_ = 0;
	}

	~BasicStack()
	{
	}

	void Push(int in) {
		stack_.push_back(in);
		head_++;

	}

	const int &top() const {
		return stack_.back();
	}

	void Pop() {
		stack_.erase(stack_.end() - 1, stack_.end());
		head_--;
	}

private:
	std::vector<int> stack_;
	int head_;
	int tail_;

};

class FloatHistogram:public Histogram
{
	friend class FriendTest;
public:
	FloatHistogram():i(0)
	{
	}

	~FloatHistogram()
	{
	}

	void Print(const FloatHistogram& fh) {
		printf("%d %d\n", fh.i,fh.note_);
	}
private:

	int i;
	
};


class FriendTest
{
	friend class FloatHistogram;
public:
	FriendTest():j(1024)
	{
	}

	~FriendTest()
	{
	}

private:
	int j;
};



class DivideandConquer
{
public:

	typedef enum alg_type{
		MERGE_SORT=0x00001,
		MAX_SUBARRAY,
		OHTER

	};
	DivideandConquer(){};
	virtual ~DivideandConquer(){};

	void Merge(std::vector<int>& array, int start_index, int middle_index, 
		int end_index){

		int len_first_part = middle_index-start_index+1, len_second_part=
			end_index-middle_index;
		std::vector<int> first_part, second_part;
		first_part.reserve(len_first_part+1);
		second_part.reserve(len_second_part+1);
		std::copy(array.begin(), array.begin()+len_first_part, first_part.begin());
		std::copy(array.begin()+len_first_part+1, array.end(), second_part.begin());

		for (int i=0;i<len_first_part;i++){
		}

		for (int j=0;j<len_second_part;j++){
		}

		first_part[len_first_part] = std::numeric_limits<int>::max();
		second_part[len_second_part] = std::numeric_limits<int>::max();
		//for last sorting operation
		for (int i=0,j=0,k=start_index;k<end_index;k++){
			if (first_part[i] <= second_part[j]){
				array[k] = first_part[i];
				i+=1;
			}else if(array[k] == second_part[j]){
				j += 1;

			}
		}



	};
	void MergeSort(std::vector<int>& array, int start_index, int end_index){
		if (start_index < end_index){
			int middle_index = (start_index + end_index)/2;
			MergeSort(array, start_index, middle_index);
			MergeSort(array, middle_index+1, end_index);
			Merge(array, start_index, middle_index,end_index);
		}

	};

	void TestMiscellous() {
		std::vector<float> a(10, 0), b(20,1);
		CalcMinDistancebetweenTwosortedArray(a, b);
	}
	void TestMergeSort(){};
	void TestExample(){
		alg_type type_of_class = MERGE_SORT;
		switch(type_of_class){
		case  MERGE_SORT:
			TestMergeSort();
			break;
		case MAX_SUBARRAY:
			break;
		default:
			std::cout << std::endl;
			break;
		};
	};


protected:
	float CalcMinDistancebetweenTwosortedArray(const std::vector<float>& a,
		const std::vector<float>&b) {
		float min_dist = std::numeric_limits<float>::max();
		int a_index = 0, b_index = 0;
		while (a_index < a.size() && b_index < b.size())
		{
			if (a[a_index] >= b[b_index]){
				min_dist = std::min(min_dist, a[a_index] - b[b_index]);
				b_index++;
			}
			else
			{
				min_dist = std::min(min_dist, b[b_index]-a[a_index] );
				a_index++;
			}
		}
		return min_dist;
	}
private:
};



class AutoDifferentiaton
{
public:
	//we use numerical optimization book, 209pages as an example
	AutoDifferentiaton():dim_x_(0)
	{
	}

	~AutoDifferentiaton()
	{
	}
	void ForwardSweepMode() {
		std::cout << "start to test forward sweep mode...\n";

	}

	void ReverseSweepMode() {
		std::cout << "start to test reverse sweep mode...\n";
	}

protected:
	void Sin(const std::vector<float>& in, std::vector<float>* in_sin) {
		if (in.size() > 0){
			in_sin->resize(in.size());
			for (int i=0;i<in.size();i++)
			{
				(*in_sin)[i] = sin(in[i]);
			}
		}
	}


	void Exp(const std::vector<float>& in, std::vector<float>* in_sin) {
		if (in.size() > 0) {
			in_sin->resize(in.size());
			for (int i = 0; i < in.size(); i++)
			{
				(*in_sin)[i] = exp(in[i]);
			}
		}
	}

	void Log10(const std::vector<float>& in, std::vector<float>* in_sin) {
		if (in.size() > 0) {
			in_sin->resize(in.size());
			for (int i = 0; i < in.size(); i++)
			{
				(*in_sin)[i] = std::log10(in[i]);
			}
		}
	}
	
	void AddOtherToSelf( std::vector<float>& in, std::vector<float>* in_sin) {
		if (in.size() > 0) {
			in_sin->resize(in.size());
			for (int i = 0; i < in.size(); i++)
			{
				in[i] += (*in_sin)[i];
			}
		}
	}
private:

	int dim_x_;

};


void TestDifferentiation(){
	AutoDifferentiaton auto_diff;
	auto_diff.ForwardSweepMode();
	auto_diff.ReverseSweepMode();

}

#endif